// Copyright (C) 2012 Volkov Zachar
// This file is part of the "zAxis Game Engine".
// For conditions of distribution and use, see copyright notice zx_Common.hpp

#include "zx_IO.hpp"
#include <Windows.h>

namespace zx {

//----------------------------------------------------------------------------//
// Log
//----------------------------------------------------------------------------//

Log::Log( const String& _name, const String& _filename, uint _mask ) : m_fs( FileSystem::CreateInstance() ),
                                                                       m_mask( _mask ),
                                                                       m_name( _name )
{
     String _path = _filename;
     if( _path.empty() )
     {
          _path = FileSystem::FullPath( FileSystem::GetAppShortName() + ".log" );
     }

     DataStreamPtr _lock = new FileStream( _path, FSF_WRITE | FSF_CREATE ); 
     _lock -> GlobalLock();
     m_file = new FileStream( _path, FSF_WRITE | FSF_CREATE | FSF_TRUNCATE );
     _lock -> GlobalUnlock();

     SYSTEMTIME _st, _lt;
     GetSystemTime( &_st );
     GetLocalTime( &_lt );

     String _msg = Format( 
      "--------------------------------------\n"
      " A log startup %02d/%02d/%04d %02d:%02d:%02d.%03d \n"
      " System time : %02d/%02d/%04d %02d:%02d:%02d.%03d \n"
      "--------------------------------------\n\n",
      int( _lt.wDay ), int( _lt.wMonth ), int( _lt.wYear ),
      int( _lt.wHour ), int( _lt.wMinute ), int( _lt.wSecond ), int( _lt.wMilliseconds ),
      int( _st.wDay ), int( _st.wMonth ), int( _st.wYear ),
      int( _st.wHour ), int( _st.wMinute ), int( _st.wSecond ), int( _st.wMilliseconds ) );

     _Write( _msg );
}
//----------------------------------------------------------------------------//
Log::~Log( void )
{
     SYSTEMTIME _st;
     GetLocalTime( &_st );

     String _msg = Format( 
      "\n--------------------------------------\n"
      " A log shutdown %02d/%02d/%04d %02d:%02d:%02d.%03d \n"
      "--------------------------------------\n",
      int( _st.wDay ), int( _st.wMonth ), int( _st.wYear ),
      int( _st.wHour ), int( _st.wMinute ), int( _st.wSecond ), int( _st.wMilliseconds ) );

     _Write( _msg );
}
//----------------------------------------------------------------------------//
void Log::EnableLevel( Level _level, bool _enabled )
{
     uint _mask = m_mask;
     _mask |= _level;
     if( !_enabled )
     {
          _mask ^= _level;
     }

     m_mask = _mask;
}
//----------------------------------------------------------------------------//
bool Log::IsEnabledLevel( Level _level ) const
{
     return ( m_mask & _level ) != 0;
}
//----------------------------------------------------------------------------//
void Log::_Write( const String& _msg, int _time_ms )
{
     if( m_file -> GlobalLock( _time_ms ) )
     {
          m_file -> Write( _msg.c_str(), _msg.length() );
          m_file -> Flush();
          m_file -> GlobalUnlock();
     }
}
//----------------------------------------------------------------------------//
void Log::Message( Level _level, const String& _msg )
{
     if( !IsEnabledLevel( _level ) ) { return; } 

     SYSTEMTIME _st;
     GetLocalTime( &_st );
     String _time = Format( "%02d:%02d:%02d", int( _st.wHour ), int( _st.wMinute ), int( _st.wSecond ) );
     String _ll;
     switch( _level )
     {
          case LL_INFO : _ll = " "; break;
          case LL_WARNING : _ll = " [Warning] "; break;
          case LL_ERROR : _ll = " [Error] "; break;
          case LL_DEBUG : _ll = " [Debug] "; break;
     };

     _Write( _time + _ll + _msg + "\n", 10000 );

     if( Logger::HasInstance() )
     {
          Logger::OnMessage( this, _level, _msg );
     }
}
//----------------------------------------------------------------------------//
void Log::Info( const String& _msg ) 
{ 
     Message( LL_INFO, _msg );
}
//----------------------------------------------------------------------------//
void Log::Warning( const String& _msg )
{ 
     Message( LL_WARNING, _msg );
}
//----------------------------------------------------------------------------//
void Log::Error( const String& _msg ) 
{ 
     Message( LL_ERROR, _msg );
}
//----------------------------------------------------------------------------//
void Log::Debug( const String& _msg ) 
{ 
     Message( LL_DEBUG, _msg );
}

//----------------------------------------------------------------------------//
// Logger
//----------------------------------------------------------------------------//

Logger::Logger( const String& _filename ) : m_fs( FileSystem::CreateInstance() ), 
                                            m_default_log( new Log( "default", _filename ) ),
                                            m_mutex( new Mutex ),
                                            m_recursion_depth( 0 ),
                                            m_enable_events( true )
{
     m_default_log -> Info( "Logger: Startup..." );
}
//----------------------------------------------------------------------------//
Logger::~Logger( void )
{
     ZX_SCOPE_LOCK( m_mutex );
     m_enable_events = false;
     m_observers.clear();
     m_default_log -> Info( "Logger: Shutdown..." );
}
//----------------------------------------------------------------------------//
Logger* Logger::CreateInstance( const String& _filename )
{
     if( !sm_instance )
     {
          new Logger( _filename );
     }

     return sm_instance;
}
//----------------------------------------------------------------------------//
LogPtr Logger::GetLog( void )
{
     return Instance().m_default_log;
}
//----------------------------------------------------------------------------//
void Logger::OnMessage( LogWeakPtr _sender, Log::Level _level, const String& _msg )
{
     LoggerEvent _le;
     _le.sender = _sender;
     _le.level = _level;
     _le.message = _msg;

     _NotifyAll( Event( ET_LOGGER, &_le ) );
}
//----------------------------------------------------------------------------//
void Logger::Message( Log::Level _level, const String& _msg )
{
     Instance().m_default_log -> Message( _level, _msg );    
}
//----------------------------------------------------------------------------//
void Logger::Info( const String& _msg )
{ 
     Message( Log::LL_INFO, _msg );
}
//----------------------------------------------------------------------------//
void Logger::Warning( const String& _msg )
{ 
     Message( Log::LL_WARNING, _msg );
}
//----------------------------------------------------------------------------//
void Logger::Error( const String& _msg )
{ 
     Message( Log::LL_ERROR, _msg );
}
//----------------------------------------------------------------------------//
void Logger::Debug( const String& _msg ) 
{ 
     Message( Log::LL_DEBUG, _msg );
}
//----------------------------------------------------------------------------//
void Logger::_BeforeEvent( void )
{
     Instance().m_mutex -> Lock();

     ++ Instance().m_recursion_depth;

     for( uint i = 0; i < Instance().m_observers_to_add.size(); ++ i )
     {
          Instance().m_observers.push_back( Instance().m_observers_to_add[i] );   
     }

     Instance().m_observers_to_add.clear();

     for( uint i = 0; i < Instance().m_observers_to_remove.size(); ++ i )
     {
          FastRemove( Instance().m_observers, (const Unknown*)Instance().m_observers_to_remove[i] );
     }

     Instance().m_observers_to_remove.clear();

     for( uint i = 0; i < Instance().m_observers.size(); ++ i )
     {
          if( !Instance().m_observers[i] )
          {
               Instance().m_observers[i] = Instance().m_observers[ Instance().m_observers.size() - 1 ];
               Instance().m_observers.pop_back();
               continue;
          }
     }
}
//----------------------------------------------------------------------------//
void Logger::_NotifyAll( const Event& _event )
{
     _BeforeEvent();
     try
     {
          if( Instance().m_enable_events && Instance().m_recursion_depth < 32 )
          for( uint i = 0; i < Instance().m_observers.size(); ++ i )
          {
               if( Instance().m_observers[i] )
               {
                    Instance().m_observers[i] -> OnEvent( _event );    
               }
          }
     }
     catch( ... ) { _AfterEvent(); throw; }
     _AfterEvent();
}
//----------------------------------------------------------------------------//
void Logger::_AfterEvent( void )
{
     -- Instance().m_recursion_depth;
     Instance().m_mutex -> Unlock();
}
//----------------------------------------------------------------------------//
void Logger::AddObserver( UnknownWeakPtr _observer )
{
     ZX_SCOPE_LOCK( Instance().m_mutex );
     Instance().m_observers_to_add.push_back( _observer );
}
//----------------------------------------------------------------------------//
void Logger::RemoveObserver( UnknownWeakPtr _observer )
{
     ZX_SCOPE_LOCK( Instance().m_mutex );
     Instance().m_observers_to_remove.push_back( _observer );
}
//----------------------------------------------------------------------------//
void Logger::EnableEvents( bool _enabled )
{
     ZX_SCOPE_LOCK( Instance().m_mutex );
     Instance().m_enable_events = _enabled;
}
//----------------------------------------------------------------------------//
bool Logger::IsEnabledEvents( void )
{
     return Instance().m_enable_events;
}

//----------------------------------------------------------------------------//
//----------------------------------------------------------------------------//

} // namespace zx
